<template>
  <div ref="canvasContainer" class="canvas-container"></div>
</template>

<script>
  import * as THREE from "three";

  //引入性能监视器stats.js
  import Stats from 'three/addons/libs/stats.module.js';

  import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
  // 添加CSS2D渲染器
  import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js';
  import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

  import imgUrl1 from '@/assets/img/5.jpg';

  import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

  export default {
    name: "快速入门",
    components: {
    },
    computed: {},
    data() {
      return {

      };
    },
    mounted() {
      // 初始化场景、相机、渲染器等
      this.initScene();
      this.createLight()

      // 创建立方体
      // this.createCube()
      // this.createCube1()

      // 创建其他形状
      this.createSphere()

      this.animate();
      this.createControls()
      // 窗口大小自适应
      window.addEventListener('resize', this.onWindowResize)
    },
    methods: {
      //  创建其他形状
      createSphere() {
        // 1.创建一个圆柱体
        // const geometry = new THREE.CylinderGeometry(10, 10, 30, 32);
        // 2.创建一个球体
        // const geometry = new THREE.SphereGeometry(10, 32, 32); // 半径为10，32个经度面，32个纬度面 
        // 3.创建一个圆锥
        // const geometry = new THREE.ConeGeometry(10, 30, 32); // 半径为10，高度为30，32个面
        // 4.创建一个圆环
        // const geometry = new THREE.RingGeometry(10, 20, 32); // 内半径为10，外半径为20，32个面
        // 5.创建一个矩形平面
        // const geometry = new THREE.PlaneGeometry(20, 20); // 宽度为20，高度为20
        // 6.创建一个圆平面
        // const geometry = new THREE.CircleGeometry(10, 32); // 半径为10，32个面        
        // const material = new THREE.MeshLambertMaterial({
        //   color: 0x00ffff, // 设置材质颜色
        //   transparent: true,//开启透明
        //   opacity: 0.5,//设置透明度
        //   side: THREE.DoubleSide,
        // });
        // const sphere = new THREE.Mesh(geometry, material); // 网格模型对象Mesh
        // sphere.position.set(0, 0, 0); // 设置球体的位置
        // this.scene.add(sphere); // 网格模型添加到场景中

        // 7.CurvePath的使用
        // const R = 10; // 圆弧半径
        // const H = 20; // 直线部分高度

        // // 创建2D曲线
        // const line1 = new THREE.LineCurve(new THREE.Vector2(R, H), new THREE.Vector2(R, 0));
        // const arc = new THREE.ArcCurve(0, 0, R, 0, Math.PI, true);
        // const line2 = new THREE.LineCurve(new THREE.Vector2(-R, 0), new THREE.Vector2(-R, H));
        // // 如果要做3D曲线应该用Vector3
        // // const line3D = new THREE.LineCurve3(
        // //   new THREE.Vector3(0, 20, 0),
        // //   new THREE.Vector3(0, 0, 10),
        // // );

        // // 多控制点的3D曲线示例
        // const points1 = [
        //   new THREE.Vector3(0, 20, 0),
        //   new THREE.Vector3(10, 10, 5),
        //   new THREE.Vector3(0, 0, 10)
        // ];
        // const curve = new THREE.CatmullRomCurve3(points1);

        // // 组合曲线路径
        // const curvePath = new THREE.CurvePath();
        // curvePath.curves.push(line1, arc, line2, curve);

        // // 生成可视化几何体
        // const points = curvePath.getPoints(50);
        // const geometry = new THREE.BufferGeometry().setFromPoints(points);


        // 8.CurvePath多段路径生成管道案例
        // 创建多段线条的顶点数据
        // const p1 = new THREE.Vector3(0, 0, 100)
        // const p2 = new THREE.Vector3(0, 0, 30);
        // const p3 = new THREE.Vector3(0, 0, 0);
        // const p4 = new THREE.Vector3(30, 0, 0);
        // const p5 = new THREE.Vector3(100, 0, 0);
        // // 1. 3D直线线段
        // const line1 = new THREE.LineCurve3(p1, p2);
        // // 2. 三维二次贝塞尔曲线
        // const curve = new THREE.QuadraticBezierCurve3(p2, p3, p4);
        // // 3. 3D直线线段
        // const line2 = new THREE.LineCurve3(p4, p5);

        // const CurvePath = new THREE.CurvePath();
        // // 三条线拼接为一条曲线
        // CurvePath.curves.push(line1, curve, line2);

        // // CurvePath:路径   40：沿着轨迹细分数  2：管道半径   25：管道截面圆细分数
        // const geometry = new THREE.TubeGeometry(CurvePath, 50, 4, 25);


        // 9.旋转成型LatheGeometry
        // Vector2表示的三个点坐标，三个点构成的轮廓相当于两端直线相连接
        // const pointsArr = [
        //   new THREE.Vector2(50, 60),
        //   new THREE.Vector2(25, 0),
        //   new THREE.Vector2(50, -60)
        // ];
        // // LatheGeometry：pointsArr轮廓绕y轴旋转生成几何体曲面
        // // pointsArr：旋转几何体的旋转轮廓形状
        // const geometry = new THREE.LatheGeometry(pointsArr, 130, 0);


        // 10.曲线生成旋转轮廓
        // 通过三个点定义一个二维样条曲线
        // const curve = new THREE.SplineCurve([
        //   new THREE.Vector2(50, 60),
        //   new THREE.Vector2(25, 0),
        //   new THREE.Vector2(50, -60)
        // ]);
        // //曲线上获取点,作为旋转几何体的旋转轮廓
        // const pointsArr = curve.getPoints(50);
        // console.log('旋转轮廓数据', pointsArr);
        // // LatheGeometry：pointsArr轮廓绕y轴旋转生成几何体曲面
        // const geometry = new THREE.LatheGeometry(pointsArr, 30);


        // 11.轮廓填充ShapeGeometry
        // 一组二维向量表示一个多边形轮廓坐标
        // const pointsArr = [
        //   new THREE.Vector2(-50, -50),
        //   new THREE.Vector2(-60, 0),
        //   new THREE.Vector2(0, 50),
        //   new THREE.Vector2(60, 0),
        //   new THREE.Vector2(50, -50),
        // ]

        // Shape表示一个平面多边形轮廓,参数是二维向量构成的数组pointsArr
        // const shape = new THREE.Shape(pointsArr);
        // const geometry = new THREE.ShapeGeometry(shape);

        // 12.拉伸ExtrudeGeometry
        //拉伸造型
        // const geometry = new THREE.ExtrudeGeometry(
        //   shape, {
        //   depth: 50,
        //   bevelThickness: 5, //倒角尺寸:拉伸方向
        //   bevelSize: 5, //倒角尺寸:垂直拉伸方向
        //   bevelSegments: 20, //倒圆角：倒角细分精度，默认3
        // }
        // );

        // 13.扫描ExtrudeGeometry
        // 扫描轮廓：Shape表示一个平面多边形轮廓
        // const shape = new THREE.Shape([
        //   // 按照特定顺序，依次书写多边形顶点坐标
        //   new THREE.Vector2(0, 0), //多边形起点
        //   new THREE.Vector2(0, 10),
        //   new THREE.Vector2(10, 10),
        //   new THREE.Vector2(10, 0),
        // ]);
        // // 扫描轨迹：创建轮廓的扫描轨迹(3D样条曲线)
        // const curve = new THREE.CatmullRomCurve3([
        //   new THREE.Vector3(-10, 0, -50),
        //   new THREE.Vector3(10, 0, 0),
        //   new THREE.Vector3(8, 50, 50),
        //   new THREE.Vector3(-5, 0, 100)
        // ]);

        // //扫描造型：扫描默认没有倒角
        // const geometry = new THREE.ExtrudeGeometry(
        //   shape, //扫描轮廓
        //   {
        //     extrudePath: curve,//扫描轨迹
        //     steps: 100//沿着路径细分精度，越大越光滑
        //   }
        // );

        // 14.绝对圆弧方法.absarc()
        // const shape = new THREE.Shape();
        // shape.lineTo(100, 0); //.currentPoint变为(100,0)
        // // absarc圆心坐标不受到.currentPoint影响，以坐标原点作为参考
        // shape.absarc(100, 0, 50, 0, Math.PI / 2); //.currentPoint变为圆弧线结束点坐标
        // shape.lineTo(0, 50);

        // const geometry = new THREE.ExtrudeGeometry(
        //   shape, //扫描轮廓
        //   {
        //     depth: 120,//拉伸长度
        //     bevelEnabled: false,//禁止倒角
        //     curveSegments: 20,//shape曲线对应曲线细分数
        //   }
        // );

        // 15.多边形Shape(内孔.holes)
        // const shape = new THREE.Shape();
        // // .lineTo(100, 0)绘制直线线段，线段起点：.currentPoint，线段结束点：(100,0)
        // shape.lineTo(100, 0);
        // shape.lineTo(100, 100);
        // shape.lineTo(0, 100);

        // const path = new THREE.Path();// 方形孔
        // path.moveTo(50, 50);
        // path.lineTo(80, 50);
        // path.lineTo(80, 80);
        // path.lineTo(50, 80);

        // //三个内孔轮廓分别插入到holes属性中
        // shape.holes.push(path);

        // const geometry = new THREE.ExtrudeGeometry(shape, {
        //   depth: 20,//拉伸长度
        //   bevelEnabled: false,//禁止倒角
        //   curveSegments: 50,
        // });


        // 16.模型边界线EdgesGeometry
        // const geometry = new THREE.CylinderGeometry(60, 60, 100, 30);
        // const material = new THREE.MeshLambertMaterial({
        //   color: 0x004444,
        //   transparent: true,
        //   opacity: 0.5,
        // });
        // const mesh = new THREE.Mesh(geometry, material);

        // // 长方体作为EdgesGeometry参数创建一个新的几何体
        // const edges = new THREE.EdgesGeometry(geometry, 3);
        // const edgesMaterial = new THREE.LineBasicMaterial({
        //   color: 0x00ffff,
        // })
        // const line = new THREE.LineSegments(edges, edgesMaterial);
        // mesh.add(line);


        // 17.几何体顶点颜色数据
        // const geometry = new THREE.BufferGeometry();
        // // 顶点坐标数组 (x,y,z,x,y,z...)
        // const vertices = new Float32Array([
        //   0, 0, 0, //顶点1坐标
        //   50, 0, 0, //顶点2坐标
        //   0, 25, 0, //顶点3坐标
        // ]);
        // // 将顶点数据添加到几何体
        // geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
        // // 顶点颜色数组 (r,g,b,r,g,b...)
        // const colors = new Float32Array([
        //   1, 0, 0,  // 顶点0红色
        //   0, 1, 0,  // 顶点1绿色
        //   0, 0, 1   // 顶点2蓝色
        // ]);
        // geometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));

        // // 顶点法线数组
        // const normals = new Float32Array([
        //   0, 0, 1,  // 顶点0法线
        //   0, 0, 1,  // 顶点1法线
        //   0, 0, 1   // 顶点2法线
        // ]);
        // geometry.setAttribute('normal', new THREE.BufferAttribute(normals, 3));
        // // 使用顶点颜色的材质
        // const material = new THREE.MeshBasicMaterial({
        //   vertexColors: true,
        //   side: THREE.DoubleSide,
        // });


        // 18.一段曲线颜色渐变
        // const curve = new THREE.CatmullRomCurve3([
        const geometry = new THREE.BufferGeometry(); //创建一个几何体对象
        // 三维样条曲线
        const curve = new THREE.CatmullRomCurve3([
          new THREE.Vector3(-50, 20, 90),
          new THREE.Vector3(-10, 40, 40),
          new THREE.Vector3(0, 0, 0),
          new THREE.Vector3(60, -60, 0),
          new THREE.Vector3(70, 0, 80)
        ]);
        const pointsArr = curve.getSpacedPoints(100); //曲线取点      
        geometry.setFromPoints(pointsArr); //pointsArr赋值给顶点位置属性 

        // 顶点颜色数组
        const colors = [];
        for (let i = 0; i < pointsArr.length; i++) {
          const color = new THREE.Color();
          color.setHSL(i / pointsArr.length, 1.0, 0.5); //设置颜色
          colors.push(color.r, color.g, color.b); //颜色数据
        }
        geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3)); //设置几何体attributes属性的color属性
        // 修改材质设置，启用顶点颜色
        const material = new THREE.LineBasicMaterial({
          vertexColors: true, // 启用顶点颜色
          linewidth: 20 // 可选：设置线宽
        });

        // 线条效果
        // const material = new THREE.LineBasicMaterial({ color: 0xffffff });
        const mesh = new THREE.Line(geometry, material);

        // 填充效果
        // const material = new THREE.MeshBasicMaterial({
        //   color: 0x00ffff,
        //   side: THREE.DoubleSide,
        // });
        // const mesh = new THREE.Mesh(geometry, material);

        // 添加到场景
        this.scene.add(mesh);
      },
      async createCube() {
        const geometry = new THREE.BoxGeometry(20, 10, 10);

        const material = new THREE.MeshLambertMaterial({
          color: new THREE.Color('orange'),
          side: THREE.DoubleSide,
        })

        this.cube = new THREE.Mesh(geometry, material)
        // 几何体xyz三个方向都放大2倍
        this.cube.scale.set(2, 2, 2)
        // 几何体绕x轴旋转
        this.cube.rotation.set(0, 1, 0)
        this.cube.position.set(10, 5, 5)
        this.scene.add(this.cube)

        // 创建大量立方体，测试性能
        // const num = 1000; //控制长方体模型数量
        // for (let i = 0; i < num; i++) {
        //   const geometry = new THREE.BoxGeometry(5, 5, 5);
        //   const material = new THREE.MeshLambertMaterial({
        //     color: 0x00ffff
        //   });
        //   const mesh = new THREE.Mesh(geometry, material);
        //   // 随机生成长方体xyz坐标
        //   const x = (Math.random() - 0.5) * 200
        //   const y = (Math.random() - 0.5) * 200
        //   const z = (Math.random() - 0.5) * 200
        //   mesh.position.set(x, y, z)
        //   this.scene.add(mesh); // 模型对象插入场景中
        // }


        // const geometry = new THREE.BoxGeometry(100, 100, 100);
        // //材质对象Material
        // const material = new THREE.MeshLambertMaterial({
        //   color: 0x00ffff, //设置材质颜色
        //   transparent: true,//开启透明
        //   opacity: 0.5,//设置透明度
        // });
        // for (let i = 0; i < 10; i++) {
        //   const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
        //   // 沿着x轴分布
        //   mesh.position.set(i * 200, 0, 0);
        //   this.scene.add(mesh); //网格模型添加到场景中
        // }
      },
      createCube1() {
        //创建一个长方体几何对象Geometry
        const geometry = new THREE.BoxGeometry(100, 100, 100);
        //材质对象Material
        const material = new THREE.MeshLambertMaterial({
          color: 0x00ffff, //设置材质颜色
          transparent: true,//开启透明
          opacity: 0.5,//设置透明度
        });
        for (let i = 0; i < 10; i++) {
          for (let j = 0; j < 10; j++) {
            const mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
            // 在XOZ平面上分布
            mesh.position.set(i * 200, 0, j * 200);
            this.scene.add(mesh); //网格模型添加到场景中  
          }
        }
      },
      initScene() {
        // 1. 创建场景
        this.scene = new THREE.Scene()
        // 设置场景背景色
        this.scene.background = new THREE.Color(0x27304D);

        // 2. 创建相机（透视相机）
        this.camera = new THREE.PerspectiveCamera(
          75,  // 1. 视野角度(FOV - Field of View)- 单位是度(°)，表示相机垂直方向的视野范围。值越大，看到的场景越多(类似广角镜头)；值越小，看到的场景越少(类似长焦镜头)。通常设置在45-75度之间。
          window.innerWidth / window.innerHeight, // 2. 宽高比(aspect ratio)- 通常设置为渲染区域的宽度除以高度(如窗口宽高比)。这个值影响水平方向的视野。
          0.1, // 3. 近裁剪面(near clipping plane)- 相机能看到的最近距离。小于这个距离的物体不会被渲染。通常设置为一个较小的正数(如0.1)。
          1000 // 4. 远裁剪面(far clipping plane) - 相机能看到的最远距离。大于这个距离的物体不会被渲染。根据场景大小设置适当的值。
        );
        this.camera.position.set(30, 30, 30)
        // lookAt()方法可以让相机指向一个特定的点，它会受OrbitControls影响
        // this.camera.lookAt(5, 0, 0);

        // 3. 创建渲染器
        this.renderer = new THREE.WebGLRenderer({
          antialias: true,
          alpha: true
        });
        this.renderer.shadowMap.enabled = true;
        // 阴影类型对比 ：
        // - THREE.BasicShadowMap - 基础阴影，性能最好但边缘锯齿明显
        // - THREE.PCFShadowMap - 使用百分比渐近过滤(PCF)，边缘更平滑
        // - THREE.PCFSoftShadowMap - (当前使用的)在PCF基础上进一步柔化边缘，效果最自然但性能消耗最大
        this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
        this.renderer.setSize(window.innerWidth, window.innerHeight)

        // 注意你的硬件设备设备像素比window.devicePixelRatio刚好是1，那么是否执行.setPixelRatio()不会有明显差异，
        // 不过为了适应不同的硬件设备屏幕，通常需要执行该方法。
        this.renderer.setPixelRatio(window.devicePixelRatio);

        // this.renderer.setClearColor(0x27304D, 1); //设置背景颜色  

        // 4. 挂载到DOM
        this.$refs.canvasContainer.appendChild(this.renderer.domElement)




        // 创建CSS2D渲染器
        this.labelRenderer = new CSS2DRenderer();
        this.labelRenderer.setSize(window.innerWidth, window.innerHeight);
        this.labelRenderer.domElement.style.position = 'absolute';
        this.labelRenderer.domElement.style.top = '0';
        this.labelRenderer.domElement.style.pointerEvents = 'none';
        this.$refs.canvasContainer.appendChild(this.labelRenderer.domElement);

        // 创建区域辅助线坐标系，X(红色)、Y(绿色)、Z(蓝色)
        const axesHelper = new THREE.AxesHelper(50);
        axesHelper.position.set(0, 0, 0);
        this.scene.add(axesHelper);

        // 初始化性能监视器
        this.stats = new Stats();
        this.stats.domElement.style.position = 'absolute';
        this.stats.domElement.style.left = '0px';
        this.stats.domElement.style.top = '0px';
        this.$refs.canvasContainer.appendChild(this.stats.domElement);
      },
      // 创建光源
      createLight() {
        // 点光源
        // const pointLight = new THREE.PointLight(0xffffff, 10000);
        // pointLight.position.set(30, 30, 30);
        // pointLight.castShadow = true;
        // this.scene.add(pointLight);

        // 点光源辅助观察
        // const pointLightHelper = new THREE.PointLightHelper(pointLight, 10);
        // this.scene.add(pointLightHelper);

        // 添加环境光
        const ambientLight = new THREE.AmbientLight(0xffffff, 2);
        this.scene.add(ambientLight);

        //增强平行光强度和添加辅助光
        const directionalLight = new THREE.DirectionalLight(0xffffff, 2);
        directionalLight.position.set(30, 0, 0);
        directionalLight.castShadow = true;
        this.scene.add(directionalLight);

        // 平行光辅助观察
        // const dirLightHelper = new THREE.DirectionalLightHelper(directionalLight, 5, 0xff0000);
        // this.scene.add(dirLightHelper);

        // 添加第二个辅助平行光
        const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5);
        directionalLight2.position.set(-10, 20, -10);
        this.scene.add(directionalLight2);

        // 添加半球光模拟环境反射
        const hemisphereLight = new THREE.HemisphereLight(0xffffff, 0x444444, 0.6);
        hemisphereLight.position.set(0, 20, 0);
        this.scene.add(hemisphereLight);

        // 创建带高光的立方体
        // const geometry = new THREE.BoxGeometry(20, 10, 10);
        // const material = new THREE.MeshPhongMaterial({
        //   color: 0x00ffff,
        //   specular: 0xffffff, // 设置高光颜色
        //   shininess: 30, // 设置高光强度
        // });
        // const cube = new THREE.Mesh(geometry, material);
        // cube.position.set(0, 0, 0);
        // this.scene.add(cube);

      },
      // 动画循环
      animate() {
        requestAnimationFrame(this.animate)

        // 性能监视器
        this.stats.update();

        // 沿x轴旋转
        // if (this.cube) {
        //   this.cube.rotation.x += 0.01
        // }

        // 渲染循环
        this.renderer.render(this.scene, this.camera)
        this.labelRenderer.render(this.scene, this.camera)
      },
      onWindowResize() {

        this.camera.aspect = window.innerWidth / window.innerHeight
        // 更新投影矩阵
        this.camera.updateProjectionMatrix()
        this.renderer.setSize(window.innerWidth, window.innerHeight)
      },
      // 相机控件
      createControls() {
        this.controls = new OrbitControls(this.camera, this.renderer.domElement)
        // 增加平移速度
        this.controls.panSpeed = 2.0;
        // 增加缩放速度
        this.controls.zoomSpeed = 1.2;
        // 增加旋转速度
        this.controls.rotateSpeed = 1.0;

        // 禁用阻尼效果
        this.controls.enableDamping = false;
        // 禁用惯性效果
        this.controls.enableInertia = false;

        // 设置平移限制
        this.controls.maxPolarAngle = Math.PI; // 允许完全垂直旋转
        this.controls.minAzimuthAngle = -Infinity;
        this.controls.maxAzimuthAngle = Infinity;

        // this.controls.target.set(10, 0, 10);
        // this.controls.update();//update()函数内会执行camera.lookAt(controls.targe)

        // 设置了渲染循环,相机控件OrbitControls就不用再通过事件change执行renderer.render(scene, camera);
        // this.controls.addEventListener('change', () => {
        //   this.renderer.render(this.scene, this.camera)
        // })
      }
    },
    unmounted() {
      window.removeEventListener('resize', this.onWindowResize)
      // if (this.renderer) {
      //   this.renderer.dispose()
      //   this.$refs.canvasContainer.removeChild(this.renderer.domElement)
      // }
    }
  };
</script>

<style lang="scss">
  * {
    margin: 0;
    padding: 0;
  }
</style>